%% misc.erl
%%      Miscellaneous functions, mostly about list handling.
%%
%% Copyright (C) 2008, 2009 Gábor Fehér
%%
%% This file is part of ECT.
%% 
%% ECT is free software: you can redistribute it and/or modify
%% it under the terms of the GNU General Public License as published by
%% the Free Software Foundation, either version 3 of the License, or
%% (at your option) any later version.
%% 
%% ECT is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%% GNU General Public License for more details.
%% 
%% You should have received a copy of the GNU General Public License
%% along with ECT.  If not, see <http://www.gnu.org/licenses/>.
%%
-module(misc).
-export([
    partition_list/2,
    add_to_list/2,
    filtermap_list/2,
    get_define_item/3]).

%% Function: partition_list(Key, List)
%% Purpose: partitions a list of tuples into more lists, according to a key element of the tuples
%% Arguments:
%%   Key::integer() - the position of the key element in the tuples
%%   List - the list of tuples to partition
%% Returns:
%%   [{Value, Items}]
%%   A list of partitions, each partition is a {Value, Items} tuple,
%%   where element(Key, X) =:= Value for each X that is an element of Items.
%%   Each Items list contains the original elements of List, and the number of such partitions is minimal.
partition_list(Key, [_|_] = List1) ->
    [Item|_] = List2 = lists:keysort(Key, List1),
    partition_list0(Key, List2, {element(Key, Item), []}, []);
partition_list(_, []) ->
    [].
partition_list0(Key, [Item|List], {LastId, LastPart}, OutList0) ->
	case element(Key, Item) of
	LastId ->
		partition_list0(Key, List, {LastId, [Item|LastPart]}, OutList0);
	NewId ->
		partition_list0(Key, List, {NewId, [Item]}, [{LastId, lists:reverse(LastPart)}|OutList0])
	end;
partition_list0(_, [], {LastId, LastPart}, OutList) ->
	lists:reverse([{LastId, lists:reverse(LastPart)}|OutList]).

%% Function: add_to_list(Item, List)
%% Purpose: adds a new item to a set of items
%% Arguments:
%%   Item - item to add
%%   List - list of items
%% Returns:
%%   * if Item was an element of List, then the result is List, but with Item moved to the first place
%%   * If Item was not an element of List, then the result is List, but with Item added at its beginning
add_to_list(Item, List) ->
    [Item
    |lists:delete(Item,List)
    ].

%% Function: filtermap_list(Fun, List)
%% Purpose: filters a list, and modifies its elements at the same time
%% Arguments:
%%   Fun - a fun that determines the filtering and mapping
%%   List - list of items to filter and map
%% Returns:
%%   For each X element of List:
%%      if Fun(X) is {ok, Value}, then Value is included in the result
%%      if Fun(X) is false, then no corresponding element is included in the result
filtermap_list(_, []) ->
    [];
filtermap_list(Fun, [Item1|List]) ->
    case Fun(Item1) of
    {ok, Item2} -> [Item2|filtermap_list(Fun, List)];
    false -> filtermap_list(Fun, List)
    end.

%% Function: get_define_item(Name, Defines, Default)
%% Purpose: looks up a value in a special list of tuples - the format of the list is the same as
%%          defines are passed to the Erlang compiler
%% Arguments:
%%   Name - name of definition to find
%%   List::[{d, Name, Value}] - list of definition
%%   Default - value to return if the definition of Name is not found
%% Returns:
%%   Value, if an item {d, Name, Value} is found in Defines
%%   Default otherwise
get_define_item(_, [], Default) ->
    Default;
get_define_item(Name, [{d, Name, Value}|_], _) ->
    Value;
get_define_item(Name, [_|Options], Default) ->
    get_define_item(Name, Options, Default).